﻿Imports System
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports System.Workflow.Interop

Namespace System.Workflow.ComponentModel.Design
    Friend NotInheritable Class WorkflowToolTip
        Implements IDisposable
        ' Methods
        Friend Sub New(ByVal parentControl As Control)
            Me.parentControl = parentControl
            Me.infoTip = New NativeToolTip(Me.parentControl.Handle)
            Me.infoTip.SetDelay(3, &H3E8)
            Me.infoTip.SetDelay(1, &H3E8)
            Me.infoTip.SetDelay(2, &HF4240)
            [Using] graphics As Graphics = Me.parentControl.CreateGraphics
                Dim tipWidth As Integer = (Convert.ToInt32(CDbl((Math.Ceiling(CDbl(graphics.MeasureString(SR.GetString("ToolTipString"), Me.parentControl.Font).Width)) / 3))) * 30)
                Me.infoTip.SetMaxTipWidth(tipWidth)
            End [Using]
            Me.inplaceTip = New NativeToolTip(Me.parentControl.Handle)
            Me.inplaceTip.SetDelay(3, 50)
            Me.inplaceTip.SetDelay(1, 50)
            Me.inplaceTip.SetDelay(2, &HF4240)
            AddHandler Me.parentControl.Layout, New LayoutEventHandler(AddressOf Me.OnParentLayoutChanged)
        End Sub

        Private Sub OnParentLayoutChanged(ByVal sender As Object, ByVal e As LayoutEventArgs)
            Me.infoTip.UpdateToolTipRectangle(Me.parentControl.ClientRectangle)
            Me.inplaceTip.UpdateToolTipRectangle(Me.parentControl.ClientRectangle)
        End Sub

        Public Sub RelayParentNotify(ByRef msg As Message)
            If (((msg.Msg = &H4E) AndAlso (msg.LParam <> IntPtr.Zero)) AndAlso Not Me.inplaceTipRectangle.IsEmpty) Then
                Dim nmhdr As NMHDR = [TryCast](Marshal.PtrToStructure(msg.LParam, GetType(NMHDR)),NMHDR)
                If (((Not nmhdr Is Nothing) AndAlso (nmhdr.hwndFrom = Me.inplaceTip.Handle)) AndAlso (nmhdr.code = NativeMethods.TTN_SHOW)) Then
                    Dim point As Point = Me.parentControl.PointToScreen(New Point(Me.inplaceTipRectangle.Left, Me.inplaceTipRectangle.Top))
                    NativeMethods.SetWindowPos(Me.inplaceTip.Handle, IntPtr.Zero, point.X, point.Y, 0, 0, &H15)
                    msg.Result = New IntPtr(1)
                End If
            End If
        End Sub

        Public Sub SetText(ByVal [text] As String, ByVal rectangle As Rectangle)
            If String.IsNullOrEmpty([text]) Then
                Me.inplaceTip.Pop
                Me.inplaceTip.Activate(False)
            Else
                Me.infoTip.Activate(False)
                Me.inplaceTip.Activate(True)
            End If
            Dim flag As Boolean = (Me.inplaceTipText <> [text])
            If (flag Or (Me.inplaceTipRectangle <> rectangle)) Then
                If NativeMethods.IsWindowVisible(Me.inplaceTip.Handle) Then
                    Me.inplaceTip.Pop
                End If
                Me.inplaceTipText = [text]
                Me.inplaceTip.UpdateToolTipText(Me.inplaceTipText)
                Me.inplaceTipRectangle = rectangle
            End If
        End Sub

        Public Sub SetText(ByVal title As String, ByVal [text] As String)
            If String.IsNullOrEmpty([text]) Then
                Me.infoTip.Pop
                Me.infoTip.Activate(False)
            Else
                Me.inplaceTip.Activate(False)
                Me.infoTip.Activate(True)
            End If
            Dim flag As Boolean = (Me.infoTipTitle <> title)
            If (flag Or (Me.infoTipText <> [text])) Then
                If NativeMethods.IsWindowVisible(Me.infoTip.Handle) Then
                    Me.infoTip.Pop
                End If
                Me.infoTipTitle = title
                Me.infoTip.UpdateTitle(Me.infoTipTitle)
                Me.infoTipText = [text]
                Me.infoTip.UpdateToolTipText(Me.infoTipText)
            End If
        End Sub

        Private Sub System.IDisposable.Dispose() Implements IDisposable.Dispose
            If (Not Me.parentControl Is Nothing) Then
                If (Not Me.infoTip Is Nothing) Then
                    DirectCast(Me.infoTip, IDisposable).Dispose
                    Me.infoTip = Nothing
                End If
                If (Not Me.inplaceTip Is Nothing) Then
                    DirectCast(Me.inplaceTip, IDisposable).Dispose
                    Me.inplaceTip = Nothing
                End If
                RemoveHandler Me.parentControl.Layout, New LayoutEventHandler(AddressOf Me.OnParentLayoutChanged)
                Me.parentControl = Nothing
            End If
        End Sub


        ' Fields
        Private infoTip As NativeToolTip
        Private infoTipText As String = String.Empty
        Private infoTipTitle As String = String.Empty
        Private inplaceTip As NativeToolTip
        Private inplaceTipRectangle As Rectangle
        Private inplaceTipText As String = String.Empty
        Private parentControl As Control

        ' Nested Types
        Private NotInheritable Class NativeToolTip
            Inherits NativeWindow
            Implements IDisposable
            ' Methods
            Friend Sub New(ByVal parentHandle As IntPtr)
                Me.parentHandle = parentHandle
                Dim cp As New CreateParams { _
                    .ClassName = "tooltips_class32", _
                    .Style = -2147483645, _
                    .ExStyle = 8, _
                    .Parent = Me.parentHandle _
                }
                Me.CreateHandle(cp)
                If (IntPtr.Zero = MyBase.Handle) Then
                    Throw New NullReferenceException(SR.GetString("Error_CreatingToolTip"))
                End If
                Dim toolInfo As TOOLINFO = Me.GetToolInfo
                toolInfo.flags = &H110
                toolInfo.hwnd = Me.parentHandle
                Me.AddTool(toolInfo)
                Me.Activate(False)
            End Sub

            Public Sub Activate(ByVal activateToolTip As Boolean)
                If (Me.activate <> activateToolTip) Then
                    Me.activate = activateToolTip
                    Dim wParam As IntPtr = If(Me.activate, New IntPtr(1), New IntPtr(0))
                    NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_ACTIVATE, wParam, IntPtr.Zero)
                End If
            End Sub

            Private Function AddTool(ByVal toolInfo As TOOLINFO) As Boolean
                Return (NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_ADDTOOL, IntPtr.Zero, (toolInfo)) <> IntPtr.Zero)
            End Function

            Private Sub DelTool(ByVal toolInfo As TOOLINFO)
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_DELTOOL, IntPtr.Zero, (toolInfo))
            End Sub

            Private Function GetToolInfo() As TOOLINFO
                Dim toolinfo As TOOLINFO
                toolinfo = New TOOLINFO { _
                    .size = Marshal.SizeOf(toolinfo), _
                    .flags = 0, _
                    .hwnd = IntPtr.Zero, _
                    .id = IntPtr.Zero _
                }
                toolinfo.rect.left = toolinfo.rect.right = toolinfo.rect.top = toolinfo.rect.bottom = 0
                toolinfo.hinst = IntPtr.Zero
                toolinfo.text = New IntPtr(-1)
                toolinfo.lParam = IntPtr.Zero
                Return toolinfo
            End Function

            Public Sub Pop()
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_POP, IntPtr.Zero, IntPtr.Zero)
            End Sub

            Public Sub SetDelay(ByVal time As Integer, ByVal delay As Integer)
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_SETDELAYTIME, New IntPtr(time), New IntPtr(delay))
            End Sub

            Public Sub SetMaxTipWidth(ByVal tipWidth As Integer)
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_SETMAXTIPWIDTH, IntPtr.Zero, New IntPtr(tipWidth))
            End Sub

            Private Sub System.IDisposable.Dispose() Implements IDisposable.Dispose
                If (Me.parentHandle <> IntPtr.Zero) Then
                    Dim toolInfo As TOOLINFO = Me.GetToolInfo
                    toolInfo.hwnd = Me.parentHandle
                    Me.DelTool(toolInfo)
                    Me.DestroyHandle
                    Me.parentHandle = IntPtr.Zero
                End If
            End Sub

            Public Sub UpdateTitle(ByVal title As String)
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_SETTITLE, New IntPtr(0), Marshal.StringToBSTR(title))
            End Sub

            Public Sub UpdateToolTipRectangle(ByVal rectangle As Rectangle)
                Dim toolInfo As TOOLINFO = Me.GetToolInfo
                toolInfo.hwnd = Me.parentHandle
                toolInfo.rect.left = rectangle.Left
                toolInfo.rect.top = rectangle.Top
                toolInfo.rect.right = rectangle.Right
                toolInfo.rect.bottom = rectangle.Bottom
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_NEWTOOLRECT, IntPtr.Zero, (toolInfo))
            End Sub

            Public Sub UpdateToolTipText(ByVal toolTipText As String)
                Dim toolInfo As TOOLINFO = Me.GetToolInfo
                toolInfo.hwnd = Me.parentHandle
                toolInfo.text = Marshal.StringToBSTR(toolTipText)
                NativeMethods.SendMessage(MyBase.Handle, NativeMethods.TTM_UPDATETIPTEXT, IntPtr.Zero, (toolInfo))
            End Sub


            ' Fields
            Private activate As Boolean = True
            Private parentHandle As IntPtr
            Private Const ToolTipClass As String = "tooltips_class32"
        End Class
    End Class
End Namespace

